package hasthi.tools.analyze;

import hasthi.actuators.ActionCenter;
import hasthi.actuators.ActionContext;
import hasthi.actuators.ManagementAction;
import hasthi.actuators.SystemActionsScheduler;
import hasthi.actuators.actions.ActionCallback;
import hasthi.common.BaseContext;
import hasthi.common.HasthiError;
import hasthi.common.HasthiException;
import hasthi.common.InvaliedResourceDataException;
import hasthi.common.Utils;
import hasthi.decision.Enviorment;
import hasthi.decision.RuleEngine;
import hasthi.instrument.JavaInstrumentation;
import hasthi.metamodel.resource.EditableManagedResource;
import hasthi.metamodel.resource.ManagedResource;
import hasthi.metamodel.resource.ManagedResource.ResourceType;
import hasthi.tools.TestingUtils;

import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;

import javax.xml.namespace.QName;

public class RulePerfSuite {
     
    
    private  static Random random = new Random();
    final private RuleEngine engine;
    private TestingUtils testingUtils  = new TestingUtils(new BaseContext());
    //private int repeatCount = 200;
    
    public RulePerfSuite(String file) throws HasthiException{
        engine = new RuleEngine(file,new DummyActionCenter());
    }
    
//    public void runOneTest() throws Exception{
//        List<ManagedResource> resoureces = createResourceSet();
//        testRules(resoureces);
//    }
    
    
//    public void runTestSuite() throws InvaliedResourceDataException,HasthiException{
//        int[] resourceCounts = new int[]{10,20,50,100,500, 1000, 5000, 10000, 20000, 30000, 50000 };
//        List<ManagedResource> resoureces = createResourceSet();
//        testRules(resoureces);
//        for(int j = 1;j< resourceCounts.length;j++){
//            for(int i = 0;i<(resourceCounts[j] - resourceCounts[j-1]);i++){
//                resoureces.add(createRandomResource(ResourceType.TransientService, new QName("AppService")));
//            }
//            testRules(resoureces);
//        }
//    }
//    
//
//    public void testRules(List<ManagedResource> resoureces) throws HasthiException{
//        DataCollector dc = new DataCollector(repeatCount);
//        dc.setNano(true);
//       
//        for(int i = 0;i<repeatCount;i++){
//            engine.resetSession();
//            for(ManagedResource resource:resoureces){
//                engine.addObject(resource);    
//            }
//
//            dc.startClock();
//            engine.fire();
//            dc.endClock();
//            
//            if(i % 100 == 0){
//                System.out.print(i + " ");    
//            }
//        }
//        DataSet dataSet = new DataSet("rules("+resoureces.size()+")");
//        dataSet.setSendData(false);
//        dataSet.addData(dc);
//        dataSet.setNormalize(1000000);
//        System.out.println(dataSet.getName());
//        dataSet.sendSummery();
//            //LocalRes 
//    }
    
    
    public void runRuleOnce(List<ManagedResource> resources) throws HasthiException{
        //engine.resetSession();
        for(ManagedResource resource:resources){
            engine.addObject(resource);    
        }
        long start = System.nanoTime();
        engine.fire();
        System.out.print((System.nanoTime() - start)/1000000+ " ");
        System.out.flush();
    }
    
    public void runRuleWithoutRest(List<ManagedResource> resources, int repeatCount) throws HasthiException{
        runRuleWithoutRest(resources,repeatCount,1.0);
    }
    
    public void runRuleWithoutRest(List<ManagedResource> resources, int repeatCount,double changeProb) throws HasthiException{
        //engine.resetSession();
        for(ManagedResource resource:resources){
            engine.addObject(resource);    
        }
        
        int count = 0;
        for(int j = 0;j< repeatCount;j++){
            testingUtils.clearChangeBuffer();
            long start = System.nanoTime();
            engine.fire();
            long end = System.nanoTime();
            System.out.print( Math.round((double)(end - start)/1000000)+ "("+JavaInstrumentation.getMemoryUsage()+") ");
            count = 0;
            for(ManagedResource resource:resources){
                if(Utils.selectWithProbability((float)changeProb)){
                    count++;
                    testingUtils.performChange((EditableManagedResource)resource);
                    engine.objectChanged(resource);
                }
            }
            System.out.println("["+count+" changed]");
        }
        System.out.flush();
        
        
    }
    
    public static class DummyActionCenter extends SystemActionsScheduler {
        private List<ManagementAction> actions = new Vector<ManagementAction>();

        @Override
        public void invoke(ManagementAction action, ActionCallback callback) throws HasthiException {
            action.init(actionContext);
            actions.add(action);
            callback.actionSucessful(action);
        }

        @Override
        public void invoke(ManagementAction action) throws HasthiException {
            action.init(actionContext);
            actions.add(action);
        }

        public List<ManagementAction> getActions() {
            return actions;
        }

        @Override
        public void planAndExecuate() throws HasthiException {
        }
        
        public Map getMap(){
            return map;
        }
    }
    public static List<ManagedResource> createRandomResources(int resourceCount,
            TestingUtils utils) throws InvaliedResourceDataException, HasthiException {
        int groupCount = Math.max(1, resourceCount/1000);
        List<ManagedResource> resources = utils.createResourceSet(groupCount);
        
        int newResource2Add = resourceCount - resources.size(); 
        int group = 0;
        for(int j = 0;j< newResource2Add;j++){
            QName type = new QName("http://extreme.indiana.edu/dummyservice/",
                    TestingUtils.APP_SERVICES[random.nextInt(TestingUtils.APP_SERVICES.length)]);
            group = (group+1)%groupCount;
            resources.add(utils.createRandomResource(ResourceType.TransientService, 
                    type,String.valueOf(group)));
        }
        if(resourceCount > resources.size()){
            throw new HasthiError("Error in perf Code");
        }
        return resources;
    }
    
    public TestingUtils getTestingUtils() {
        return testingUtils;
    }

    public static void main(String[] args) throws Exception {
//        String file = null;
//        if(args.length == 0){
//            file = "resources/rules/lead.drl";
//        }else{
//            file = new File(args[0]).getAbsolutePath();
//        }
//        System.out.println("4 Rules");
//        RulePerfSuite testSuite = new RulePerfSuite("/u/hperera/svncode/management/resources/rules/lead4.drl");
//        testSuite.runTestSuite();
//        System.out.println("8 Rules");
//        testSuite = new RulePerfSuite("/u/hperera/svncode/management/resources/rules/lead.drl");
//        testSuite.runTestSuite();

        //testSuite.runOneTest();
        
        String ruleFile = args[0];
        int resourceCount = Integer.parseInt(args[1]);
        
        RulePerfSuite testSuite = new RulePerfSuite(ruleFile);
        System.out.println("rc="+resourceCount);
        List<ManagedResource> resources = createRandomResources(resourceCount, testSuite.getTestingUtils());
        if(resourceCount > resources.size()){
            throw new HasthiError("Error in perf Code");
        }
        testSuite.runRuleOnce(resources);
        
        
        
    }

   
}
